package cz.pproproj.aaservice.aasdbmodule;

import cz.pproproj.aaservice.aasdbmodule.model.AasDbMappingDAO;
import cz.pproproj.aaservice.aasdbmodule.model.AasDbUserRoleDAO;
import cz.pproproj.aaservice.guimodule.model.AuthorizationRole;
import cz.pproproj.aaservice.guimodule.model.AuthorizationUser;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Transactional
public class DbManagementImpl implements DbManagement {

    @PersistenceContext
    private EntityManager _em;

    @Override
    public List<AuthorizationRole> getRolesFromUser(AuthorizationUser user) {

        String query = "SELECT u FROM AasDbMappingDAO u WHERE u.dataSource = :dataSource and u.userName = :userName";
        AasDbMappingDAO dbUser = _em.createQuery(query, AasDbMappingDAO.class)
                .setParameter("dataSource", user.getDataSource())
                .setParameter("userName", user.getUserName())
                .getSingleResult();

        List<AasDbUserRoleDAO> dbRoles = dbUser.getRoles();

        return transformDAOToAuthorizationRoles(dbRoles);

    }

    @Override
    public List<AuthorizationRole> getAllRoles() {

        String query = "SELECT r FROM AasDbUserRoleDAO r";
        List<AasDbUserRoleDAO> dbRoles = _em.createQuery(query, AasDbUserRoleDAO.class).getResultList();

        return transformDAOToAuthorizationRoles(dbRoles);

    }

    @Override
    public boolean removeRole(AuthorizationRole role) {

        String query = "delete AasDbUserRoleDAO r where r.name = :roleName";
        int changesCount = _em.createQuery(query).setParameter("roleName", role.getRoleName()).executeUpdate();

        return changesCount > 0;

    }

    @Override
    public boolean addRole(AuthorizationRole role) {
        AasDbUserRoleDAO newRole = new AasDbUserRoleDAO(role.getRoleName());
        _em.persist(newRole);

        return true;

    }

    @Override
    public boolean removeMapping(AuthorizationUser user, List<AuthorizationRole> roles) {

        String query = "SELECT u FROM AasDbMappingDAO u WHERE u.dataSource = :dataSource and u.userName = :userName";
        AasDbMappingDAO dbUser = _em.createQuery(query, AasDbMappingDAO.class)
                .setParameter("dataSource", user.getDataSource())
                .setParameter("userName", user.getUserName())
                .getSingleResult();

        List<AasDbUserRoleDAO> daoRoles = dbUser.getRoles();
        Set<AasDbUserRoleDAO> daoRolesToRemove = transformAuthorizationRolesToDAOSet(roles);

        daoRoles.removeAll(daoRolesToRemove);

        _em.persist(dbUser);
        return true;
    }

    @Override
    public boolean addMapping(AuthorizationUser user, List<AuthorizationRole> roles) {

        String query = "SELECT u FROM AasDbMappingDAO u WHERE u.dataSource = :dataSource and  u.userName = :userName";
        AasDbMappingDAO dbUser = _em.createQuery(query, AasDbMappingDAO.class).setParameter("userName", user.getUserName()).getSingleResult();

        if (dbUser == null) {
            dbUser = new AasDbMappingDAO(user.getDataSource(), user.getUserName());
        }

        List<AasDbUserRoleDAO> dbRoles = dbUser.getRoles();
        List<AasDbUserRoleDAO> toAdd = transformAuthorizationRolesToDAO(roles);

        for (AasDbUserRoleDAO dbRole : toAdd) {
            if (!dbRoles.contains(dbRole)) {
                dbRoles.add(dbRole);
            }
        }

        _em.persist(dbUser);

        return toAdd.size() > 0;
    }

    @Override
    public boolean authorize(AuthorizationUser user, AuthorizationRole role) {
        String query = "SELECT u FROM AasDbMappingDAO u WHERE u.dataSource = :dataSource and  u.userName = :userName";
        AasDbMappingDAO dbUser = _em.createQuery(query, AasDbMappingDAO.class)
                .setParameter("dataSource", user.getDataSource())
                .setParameter("userName", user.getUserName())
                .getSingleResult();

        List<AasDbUserRoleDAO> dbRoles = dbUser.getRoles();
        List<AuthorizationRole> authorizationRoles = transformDAOToAuthorizationRoles(dbRoles);
        return authorizationRoles.contains(role);
    }

    @Override
    public Map<AuthorizationUser, List<AuthorizationRole>> getAllMappings() {

        String query = "SELECT u FROM AasDbMappingDAO u";
        List<AasDbMappingDAO> dbMappings = _em.createQuery(query, AasDbMappingDAO.class).getResultList();

        Map<AuthorizationUser, List<AuthorizationRole>> mappings = new HashMap<>();

        for (AasDbMappingDAO mapping : dbMappings) {
            List<AuthorizationRole> roles = transformDAOToAuthorizationRoles(mapping.getRoles());
            mappings.put(new AuthorizationUser(mapping.getDataSource(), mapping.getUserName()), roles);
        }

        return mappings;
    }

    private List<AuthorizationRole> transformDAOToAuthorizationRoles(List<AasDbUserRoleDAO> daoRoles) {
        List<AuthorizationRole> roles = new ArrayList<>();
        for (AasDbUserRoleDAO roleDAO : daoRoles) {
            roles.add(new AuthorizationRole(roleDAO.getName()));
        }
        return roles;
    }

    private List<AasDbUserRoleDAO> transformAuthorizationRolesToDAO(List<AuthorizationRole> authRoles) {
        List<AasDbUserRoleDAO> daoRoles = new ArrayList<>();
        for (AuthorizationRole authRole : authRoles) {
            daoRoles.add(_em.getReference(AasDbUserRoleDAO.class, authRole.getRoleName()));
        }
        return daoRoles;
    }

    private Set<AasDbUserRoleDAO> transformAuthorizationRolesToDAOSet(List<AuthorizationRole> authRoles) {
        Set<AasDbUserRoleDAO> daoRoles = new HashSet<>();
        for (AuthorizationRole authRole : authRoles) {
            daoRoles.add(_em.getReference(AasDbUserRoleDAO.class, authRole.getRoleName()));
        }
        return daoRoles;
    }
}
